package com.github.llyb120.qiuya.storage;

import com.github.llyb120.qiuya.error.SizeNotEnoughException;
import sun.misc.Cleaner;
import sun.nio.ch.DirectBuffer;

import java.io.File;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public final class FileRect implements AutoCloseable{
    private FileChannel channel;
    private String filePath;
    private File file;
    private MappedByteBuffer buffer;
    private int fileSize;
//    private int free = -1;
    private long totalSize;
    private int index;
//    private static final byte[] Sign = "qiuya".getBytes();
//    public FileRect(String path, int fileSize, long offset)  {
//        this(new File(path), fileSize, offset);
//    }

    public FileRect(File file, int fileSize, int index, long offset)  {
        this.index = index;
        this.totalSize = offset;
        this.file = file;
        File dir = file.getParentFile();
        if(!dir.exists()){
            dir.mkdirs();
        }
        boolean isNew = !file.exists();
        if(isNew){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
//            free = fileSize - getSignOffset();
            this.fileSize = fileSize;
        } else {
            this.fileSize = (int) file.length();
        }
//        if(fileSize < getSignOffset()){
//            throw new IOException("文件大小应至少为" + getSignOffset()) ;
//        }
        filePath = file.getAbsolutePath();
        try(FileChannel channel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ, StandardOpenOption.WRITE)){
            this.channel = channel;
            buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
//        channel = //new RandomAccessFile(filePath, "rw").getChannel();
//        channel.close();
        //读取free空间
//        if(isNew){
            //写入校验
//            writeBytes(0, Sign);
            //写入剩余
//            writeFree();
//        } else {
            //验证文件格式
//            if(!Arrays.equals(Sign, readBytes(0, Sign.length))){
//                throw new IOException("文件格式错误: " + filePath);
//            }
//            free = readInt(Sign.length);
//        }
    }


//    public FileRect readMode(int offset){
//        if (mode != BufferMode.READ) {
//            buffer.flip();
//            mode = BufferMode.READ;
//        }
//        buffer.position(offset);
//        buffer.limit(fileSize);
//        return this;
//    }
//
//
//    public FileRect writeMode(int offset){
//        if (mode != BufferMode.WRITE) {
//            buffer.flip();
//            mode = BufferMode.WRITE;
//        }
//        buffer.position(offset);
//        return this;
//    }

    /************** support*******************/
//    public int getSignOffset(){
//        //sign + free
//        return Sign.length + Integer.BYTES;
//    }


    public long getTotalSize() {
        return totalSize;
    }

    public int getIndex() {
        return index;
    }

    public int getFileSize() {
        return fileSize;
    }

    /************** read ********************/
//    public int readInt(int offset){
//        readMode(offset);
//        return buffer.getInt();
//    }


    public byte[] readBytes(int offset, int len){
        return readBytes(offset, new byte[len], 0, len);
//        byte[] bs = new byte[len];
//        readMode(offset);
//        buffer.get(bs, 0, len);
//        for (int i = 0; i < len; i++) {
//            bs[i] = buffer.get(offset + i);
//        }
//        return bs;
    }

    public byte[] readBytes(int offset, byte[] bs, int start, int len){
        for (int i = 0; i < len; i++) {
            bs[start + i] = buffer.get(offset + i);
        }
        return bs;
    }

    /**************** write ****************/

//    public FileRect writeInt(int offset, int value) throws SizeNotEnoughException {
//        buffer.putInt(offset, value);
//        return this;
//    }

    public FileRect writeBytes(int offset, byte[] bs) throws SizeNotEnoughException {
        return writeBytes(offset, bs, 0, bs.length);
    }

    public FileRect writeBytes(int offset, byte[] bs, int start, int len) throws SizeNotEnoughException {
//        writeMode(offset);
        for (int i = 0; i < len; i++) {
            buffer.put(offset + i, bs[start + i]);
        }
//        buffer.put(bs, start, len);
        return this;
    }


//    public FileRect writeFree(){
//        buffer.putInt(Sign.length, free);
//        return this;
//    }


    /*************** check ****************/
//    public boolean canWrite(int len){
//        return free > len;
//    }
//    public void checkFree(int len) throws SizeNotEnoughException {
//        if (!canWrite(len)) {
//            throw new SizeNotEnoughException();
//        }
//    }

//    public byte[] readSign(){
//        byte[] bs = new byte[FileSign.length];
//        readMode(0);
//        buffer.get(bs);
//        return bs;
//    }
//
//    public FileRect writeSign(){
//        writeMode(0);
//        buffer.put(FileSign);
//        return this;
//    }

    public void close() throws Exception {
//        buffer.force();
        unmap();
//        try{
//            Method m = FileChannelImpl.class.getDeclaredMethod("unmap",
//                    MappedByteBuffer.class);
//            m.setAccessible(true);
//            m.invoke(FileChannelImpl.class, buffer);
//        } catch (Exception e){
//            e.printStackTrace();
//        }
//        cleanHandle(buffer);
//        if (channel != null) {
//            channel.close();
//        }
    }

//    public void prepareDelete(){
//        try {
//            close();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    public void delete() {
        try {
            close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.gc();
        file.delete();
    }

    private void unmap() {
        Cleaner cl = ((DirectBuffer)buffer).cleaner();
        if (cl != null)
            cl.clean();
    }



}
